#pragma once

#include "./al/zen_al.h"

#include "zen_audio.h"
#include "zen_point.h"
#include "zen_cache.h"
#include "zen_audio_wav.h"
#include "zen_audio_raw.h"
#include "zen_audio_wav.h"
#include "zen_log.h"
#include "zen_file.h"
#include <string>
#include <vector>
#include <map>

#if defined(ZEN_P_AUDIO_TRACK_LIMIT) && ZEN_P_AUDIO_TRACK_LIMIT >= 8 && ZEN_P_AUDIO_TRACK_LIMIT <= 1024
static constexpr size_t AudioTrackLimit = ZEN_P_AUDIO_TRACK_LIMIT;
#else
static constexpr size_t AudioTrackLimit = 128;
#endif

#define EMPTY_TRACK_ID 0

namespace Zen
{
    struct AudioSource
    {
        virtual ~AudioSource() = default;
    };

    class ALAudioSource : public AudioSource
    {
      protected:
        Zen::ALBuffer mBuffer;

      public:
        static std::shared_ptr<Zen::ALAudioSource> Generate(std::shared_ptr<Audio> const& audio);

        Zen::ALBuffer const& GetALBuffer() const;
    };

    using AudioTrackID = int;

    using SharedAudio = std::shared_ptr<AudioSource>;

    class AudioLoaderI
    {
      public:
        virtual std::shared_ptr<Audio> OnLoadAudio(std::string const& key) = 0;
    };

    class WavAudioLoader : public AudioLoaderI
    {
      public:
        virtual std::shared_ptr<Audio> OnLoadAudio(std::string const& key) override;
    };

    class AudioCache : public Cache<std::string, ALAudioSource>
    {
      public:
        AudioCache();

        void SetDelegate(std::shared_ptr<AudioLoaderI> delegate);

        std::shared_ptr<Zen::ALAudioSource> AddAudio(std::string const& key, std::shared_ptr<Audio> const& audio);

      protected:
        virtual std::shared_ptr<Zen::ALAudioSource> LoadData(std::string const& key) override;

        std::shared_ptr<AudioLoaderI> mDelegate;
    };

    class AudioPlayer
    {
      public:
        static std::shared_ptr<AudioPlayer> CreatePlayer();

        AudioPlayer() = default;

        virtual ~AudioPlayer() = default;

        /// Play
        virtual AudioTrackID Play(std::shared_ptr<ALAudioSource> audio, bool loop, float volume = 1.f, float pitch = 1.f, Zen::Point3 position = { 0, 0, 0 }, Zen::Point3 velocity = { 0, 0, 0 }) = 0;

        virtual bool ResetVolume(AudioTrackID, float v) = 0;
        virtual bool ResetPitch(AudioTrackID, float p) = 0;
        virtual bool ResetPosition(AudioTrackID, Zen::Point3) = 0;
        virtual bool ResetVelocity(AudioTrackID, Zen::Point3) = 0;

        virtual bool Pause(AudioTrackID) = 0;

        virtual bool Resume(AudioTrackID) = 0;

        virtual bool Stop(AudioTrackID) = 0;

        virtual bool IsValid(AudioTrackID) = 0;

        virtual void PausePlayer() = 0;

        virtual void ResumePlayer() = 0;

        virtual void StopAllTracks() = 0;

        virtual bool IsPlayerPaused() = 0;

        virtual std::shared_ptr<AudioCache> GetCache() = 0;
    };
} // namespace Zen
